#include "stdafx.h"
#include "HttpClient.h"
#include "CommonUtil.h"

static std::atomic_int32_t gReqId;

req_flag_ptr HttpClient::create_req_flag() {
    return std::make_shared<__req_flag>();
}

//////
XResponse::XResponse(uint64_t reqId, req_flag_weak_ptr flag, HttpResponse callback) {
    m_ReqId = reqId;
    m_flag = flag;
    m_callback = callback;
}

XResponse::XResponse() {
}

XResponse::~XResponse() {
}

////
int XResponse::GetErrorCode()const {

    if (m_rawResponse.error.code != cpr::ErrorCode::OK) {
        return (int)m_rawResponse.error.code;
    }
    
    if (m_rawResponse.status_code != 200) {
        return m_rawResponse.status_code;
    }
    return -1;
    //return m_resp.result().code();;
}

bool XResponse::isOK()const {
    return m_rawResponse.error.code == cpr::ErrorCode::OK
           && m_rawResponse.status_code == 200;
    //&& m_resp.result().code() == 0;
}

bool XResponse::needReq()const {
    return !m_flag.expired();
}

std::string XResponse::GetErrorType() const {
    if (m_rawResponse.error.code != cpr::ErrorCode::OK || m_rawResponse.status_code != 200) {
        return "Network Error";
    }
    
    return "System Error";
}

std::string XResponse::GetErrorMsg()const {
    if (m_rawResponse.error.code != cpr::ErrorCode::OK) {
        std::string errMsg = "";
        if (m_rawResponse.error.code == cpr::ErrorCode::CONNECTION_FAILURE
                || m_rawResponse.error.code == cpr::ErrorCode::HOST_RESOLUTION_FAILURE
                || m_rawResponse.error.code == cpr::ErrorCode::NETWORK_RECEIVE_ERROR
                || m_rawResponse.error.code == cpr::ErrorCode::NETWORK_SEND_FAILURE) {
            errMsg = "network is unavailable. Please Check for the network problem.";
        } else {
            errMsg = m_rawResponse.error.message;
        }
        
        return errMsg;
    }
    
    if (m_rawResponse.status_code != 200) {
        return m_rawResponse.reason;
    }
    
    return "";
    //  return m_resp.result().msg();
}


std::string XResponse::getHash() const {
    return m_hash;
}

uint64_t XResponse::GetReqId()const {
    return m_ReqId;
}

void XResponse::SetResponse(const cpr::Response &resp) {
    m_rawResponse = resp;
    try {
        if (resp.error.code == cpr::ErrorCode::OK && resp.status_code == 200) {
            //m_hash = CommonUtil::GetMD5(resp.text);
            //m_resp.ParseFromString(resp.text);
        }
    } catch (const std::exception &e) {
    
    }
}

void XResponse::SetReqInfo(uint64_t reqId, req_flag_weak_ptr flag, HttpResponse callback) {
    m_ReqId = reqId;
    m_flag = flag;
    m_callback = callback;
}

void XResponse::DoCallback()const {
    if (m_callback) {
        m_callback(*this);
    }
}

cpr::Response XResponse::getRawResponse()const {
    return m_rawResponse;
}

Json::Value XResponse::getJson()const {
    return CommonUtil::ToJson(m_rawResponse.text);
}



HttpClient::HttpClient(): m_threadPool(8) {
    m_host = "";
}

HttpClient::~HttpClient() {
}


void HttpClient::Get(const std::string &url, req_flag_weak_ptr flag,
                     HttpResponse callback) {
                     
                     
    auto respPtr = GetResponsePtr(flag, callback, true);
    
    if (respPtr) {
        m_threadPool.enqueue([this, url,  respPtr]() {
        
            if (!respPtr->needReq()) {
                return;
            }
            
            std::string urlReal = url;
            if (!CommonUtil::StartsWith(url, "http")) {
                urlReal = m_host + url;
            }
            
            auto pSession = getSession();
            int retryCnt = 3;
            cpr::Response resp;
            
            do {
                cpr::Header  headers;
                headers["Content-Type"] = "application/x-www-form-urlencoded";
                headers["Accept-Encoding"] = "gzip, deflate";
                headers["Proxy-Connection"] = "keep-alive";
                headers["Referer"] = "http://vip.stock.finance.sina.com.cn/mkt/";
                
                headers["Cookie"] =
                    "U_TRS1=000000ce.2601aa4.5fd8a3fe.d7941cfa; UOR=www.baidu.com,blog.sina.com.cn,; SINAGLOBAL=58.33.120.162_1608033279.392931; visited_uss=gb_cfd; UM_distinctid=17c2c2aac761a1-02d2c2ae5af95-a7d173c-384000-17c2c2aac77cd6; __gads=ID=fe0b6fe1e19d81f3-220bd783f0cb00cc:T=1632828568:RT=1632828568:S=ALNI_MbdrMzyE_el9uKw6J2iZPQRfLFqAw; SR_SEL=1_511; MONEY-FINANCE-SINA-COM-CN-WEB5=; ULV=1636521753596:12:2:2::1636440378799";
                    
                headers["User-Agent"] =
                    "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/95.0.4638.69 Safari/537.36";
                pSession->SetHeader(headers);
                
                pSession->SetUrl(cpr::Url(urlReal));
                resp = pSession->Get();
                
                if (resp.error.code != cpr::ErrorCode::OPERATION_TIMEDOUT) {
                    break;
                }
                retryCnt--;
            } while (retryCnt > 0);
            
            saveSession(pSession);
            respPtr->SetResponse(resp);
            
            std::lock_guard<std::mutex> locker(m_mutex);
            m_responses.push_back(respPtr);
            
        });
    }
}

void HttpClient::Process() {
    std::vector<XResponsePtr> responses;
    {
        try {
            std::lock_guard<std::mutex> locker(m_mutex);
            responses = m_responses;
            m_responses.clear();
        } catch (const std::exception &e) {
            SOUI::SStringA ssLogText;
            ssLogText.Format("XHttpClient::Process locker: %s\r\n", e.what());
            OutputDebugStringA(ssLogText);
        }
        
    }
    
    for (size_t i = 0; i < responses.size(); i++) {
        if (responses[i]->needReq()) {
            responses[i]->DoCallback();
        }
    }
}

XResponsePtr HttpClient::GetResponsePtr(req_flag_weak_ptr flag, HttpResponse callback, bool inorgeLogin) {
    return std::make_shared<XResponse>(gReqId++, flag, callback);
}

std::shared_ptr<cpr::Session> HttpClient::getSession() {
    SesstionPtr pSesstion;
    std::lock_guard<std::mutex> ___(m_mtx_session);
    
    if (!m_sessions.empty()) {
        pSesstion = m_sessions.front();
        cpr::Header  headers;
        headers["Content-Type"] = "application/json";
        pSesstion->SetHeader(headers);
        m_sessions.pop_front();
    } else {
        pSesstion = std::make_shared<cpr::Session>();
        pSesstion->SetVerifySsl(cpr::VerifySsl(false));
        pSesstion->SetTimeout(cpr::Timeout(10000));
    }
    
    return pSesstion;
}

void HttpClient::saveSession(std::shared_ptr<cpr::Session> pSession) {
    if (!pSession) {
        return;
    }
    std::lock_guard<std::mutex> ___(m_mtx_session);
    m_sessions.push_back(pSession);
}

void HttpClient::Init(HWND hWnd) {
    m_hWnd = hWnd;
}

void HttpClient::Post(const std::string &protoId, req_flag_weak_ptr flag,
                      const Json::Value &msg,
                      HttpResponse callback) {
                      
    auto respPtr = GetResponsePtr(flag, callback, true);
    
    if (respPtr) {
        std::string buf = CommonUtil::ToString(msg);
        
        m_threadPool.enqueue([this, protoId, buf, respPtr]() {
            if (!respPtr->needReq()) {
                return;
            }
            PostToServer(protoId, buf, respPtr, 10000);
        });
    }
}



void HttpClient::PostToServer(const std::string &protoId,
                              const std::string &msgBuf,
                              XResponsePtr  respPtr,
                              uint32_t timeout) {
                              
                              
    auto pSession = getSession();
    int retryCnt = 3;
    cpr::Response resp;
    
    do {
    
        cpr::Header  headers;
        headers["Content-Type"] = "application/json";
        headers["X-Protocol-Id"] = protoId;
        headers["X-Request-Id"] = std::to_string(respPtr->GetReqId());
        
        pSession->SetHeader(headers);
        
        pSession->SetUrl(cpr::Url(m_host));
        pSession->SetBody(cpr::Body(msgBuf));
        resp = pSession->Post();
        
        if (resp.error.code != cpr::ErrorCode::OPERATION_TIMEDOUT) {
            break;
        }
        retryCnt--;
    } while (retryCnt > 0);
    
    saveSession(pSession);
    respPtr->SetResponse(resp);
    
    std::lock_guard<std::mutex> locker(m_mutex);
    m_responses.push_back(respPtr);
    
    
}

HttpClient &getHttpClient() {
    static HttpClient httpClient;
    return httpClient;
}
